package com.lyf.scm.common.util;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.handler.WriteHandler;
import com.alibaba.excel.write.merge.LoopMergeStrategy;
import com.alibaba.fastjson.JSON;
import com.lyf.scm.common.enums.ResponseMsg;
import com.lyf.scm.common.model.ExcelExportDataDTO;
import com.lyf.scm.common.util.style.DayLongestMatchColumnWidthStyleStrategy;
import com.lyf.scm.common.util.style.LongestMatchColumnWidthStyleStrategy;
import com.rome.arch.core.exception.RomeException;
import feign.Response;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.util.Collection;
import java.util.Date;
import java.util.List;

/**
 * excel导出工具类
 *
 * @author wangchlei
 * @date 2020/08/16
 */
@Slf4j
public class ExcelExportUtil {
    /**
     * 响应头
     */
    public static final String HEAD_CONTENT_TYPE = "content-type";
    private static final String HEAD_CONTENT_DISPOSITION = "Content-disposition";
    /**
     * 响应头结果
     */
    public static final String JSON_RESPONSE = "application/json;charset=utf-8";
    public static final String EXCEL_RESPONSE = "application/vnd.ms-excel;charset=utf-8";
    /**
     * 响应头响应类型结果
     */
    public static final String HEAD_CONTENT_TYPE_JSON = "application/json";
    public static final String HEAD_CONTENT_TYPE_EXCEL = "application/vnd.ms-excel";
    /**
     * 字符集
     */
    public static final String CHARACTER_ENCODING_UTF8 = "utf-8";
    /**
     * excel后缀
     */
    public static final String SUFFIX_EXCEL_2007 = ".xlsx";

    /**
     * 合并excel的Method
     */
    public static Method METHOD_REGISTERWRITEHANDLER = null;

    static {
        try {
            METHOD_REGISTERWRITEHANDLER = ExcelWriterBuilder.class.getMethod("registerWriteHandler", WriteHandler.class);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    /**
     * 其它拼接字符串
     */
    public static final String SPLICING_ATTACHMENT = "attachment;filename=";


    /**
     * 判断应用相应格式是否是application/json
     *
     * @param headersMap
     * @return {@link boolean}
     **/
    public static boolean validJsonResponse(Collection<String> headersMap) {
        if (CollectionUtils.isEmpty(headersMap)) {
            return false;
        }
        return headersMap.contains(JSON_RESPONSE);
    }

    /**
     * 判断应用相应格式是否是application/vnd.ms-excel
     *
     * @param headersMap
     * @return {@link boolean}
     **/
    public static boolean validExcelResponse(Collection<String> headersMap) {
        if (CollectionUtils.isEmpty(headersMap)) {
            return false;
        }
        return headersMap.contains(EXCEL_RESPONSE);
    }

    /**
     * 处理异常结果（不含excel文件）
     *
     * @param response
     * @param httpServletResponse
     * @return void
     **/
    public static void handleProviderErrorResponse(Response response, HttpServletResponse httpServletResponse) {
        setCharacterAndReturnType(httpServletResponse, HEAD_CONTENT_TYPE_JSON);
        try {
            httpServletResponse.getWriter().println(response.body());
        } catch (IOException ioException) {
            ioException.printStackTrace();
        }
    }

    /**
     * 处理正确结果（含excel文件）
     *
     * @param response
     * @param httpServletResponse
     * @param sourceFileName
     * @return void
     **/
    public static void handleProviderRightResponse(Response response, HttpServletResponse httpServletResponse, String sourceFileName) {
        InputStream fileInputStream;
        OutputStream outStream;
        try {
            String fileName = URLEncoder.encode(sourceFileName, CHARACTER_ENCODING_UTF8);
            Response.Body body = response.body();
            setCharacterAndReturnType(httpServletResponse, HEAD_CONTENT_TYPE_EXCEL);
            httpServletResponse.setHeader(HEAD_CONTENT_DISPOSITION, SPLICING_ATTACHMENT + fileName + SUFFIX_EXCEL_2007);
            fileInputStream = body.asInputStream();
            outStream = httpServletResponse.getOutputStream();
            byte[] bytes = new byte[1024];
            int len;
            while ((len = fileInputStream.read(bytes)) != -1) {
                outStream.write(bytes, 0, len);
            }
            fileInputStream.close();
            outStream.close();
            outStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 设置字符集及返回类型
     *
     * @param httpServletResponse
     * @param headContentType
     * @return void
     **/
    public static void setCharacterAndReturnType(HttpServletResponse httpServletResponse, String headContentType) {
        httpServletResponse.setContentType(headContentType);
        httpServletResponse.setCharacterEncoding(CHARACTER_ENCODING_UTF8);
    }

    /**
     * 将处理异常结果返回给消费者客户端
     *
     * @param httpServletResponse
     * @param e
     * @return void
     **/
    public static void handleExceptionResponse(HttpServletResponse httpServletResponse, Exception e) {
        // 重置response
        httpServletResponse.reset();
        ExcelExportUtil.setCharacterAndReturnType(httpServletResponse, ExcelExportUtil.HEAD_CONTENT_TYPE_JSON);
        com.rome.arch.core.clientobject.Response<?> romeResponse;
        e.printStackTrace();
        if (e instanceof RomeException) {
            romeResponse = com.rome.arch.core.clientobject.Response.builderFail(((RomeException) e).getCode(), e.getMessage());
        } else if (e instanceof feign.RetryableException) {
            romeResponse = com.rome.arch.core.clientobject.Response.builderFail(ResponseMsg.EXCEPTION_SC_1004.getCode(), ResponseMsg.EXCEPTION_SC_1004.getMsg());
        } else {
            if (StringUtils.isEmpty(e.getMessage())) {
                romeResponse = com.rome.arch.core.clientobject.Response.builderFail(ResponseMsg.FAIL.getCode(), "未知错误");
            } else {
                romeResponse = com.rome.arch.core.clientobject.Response.builderFail(ResponseMsg.FAIL.getCode(), e.getMessage());
            }
        }
        try {
            httpServletResponse.getWriter().println(JSON.toJSONString(romeResponse));
        } catch (IOException ioException) {
            ioException.printStackTrace();
        }
    }

    /**
     * 导出excel
     *
     * @param httpServletResponse
     * @param excelExportDataDTO
     * @return void
     **/
    public static void exportExcel(HttpServletResponse httpServletResponse, ExcelExportDataDTO excelExportDataDTO) throws Exception {
        ExcelExportUtil.setCharacterAndReturnType(httpServletResponse, ExcelExportUtil.HEAD_CONTENT_TYPE_EXCEL);
        //合并单元格
        ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(httpServletResponse.getOutputStream()).head(excelExportDataDTO.getHeads()).registerWriteHandler(excelExportDataDTO.getWriteHandler()).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy());
        for (int i = 0; i < excelExportDataDTO.getMergeCellsColumns(); i++) {
            LoopMergeStrategy loopMergeStrategy = new LoopMergeStrategy(excelExportDataDTO.getTargetsSize(), i);
            ExcelExportUtil.METHOD_REGISTERWRITEHANDLER.invoke(excelWriterBuilder, loopMergeStrategy);
        }
        excelWriterBuilder.sheet("sheet1").doWrite(excelExportDataDTO.getExportDataList());
    }

    /**
     * 导出excel
     *
     * @param httpServletResponse
     * @param excelExportDataDTO
     * @return void
     **/
    public static void exportExcel(HttpServletResponse httpServletResponse, ExcelExportDataDTO excelExportDataDTO, String sourceFileName) throws Exception {
        String fileName = URLEncoder.encode(sourceFileName, CHARACTER_ENCODING_UTF8);
        setCharacterAndReturnType(httpServletResponse, HEAD_CONTENT_TYPE_EXCEL);
        httpServletResponse.setHeader(HEAD_CONTENT_DISPOSITION, SPLICING_ATTACHMENT + fileName + SUFFIX_EXCEL_2007);
        //合并单元格
        ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(httpServletResponse.getOutputStream()).head(excelExportDataDTO.getHeads()).registerWriteHandler(excelExportDataDTO.getWriteHandler()).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy());
        for (int i = 0; i < excelExportDataDTO.getMergeCellsColumns(); i++) {
            LoopMergeStrategy loopMergeStrategy = new LoopMergeStrategy(excelExportDataDTO.getTargetsSize(), i);
            ExcelExportUtil.METHOD_REGISTERWRITEHANDLER.invoke(excelWriterBuilder, loopMergeStrategy);
        }
        excelWriterBuilder.autoCloseStream(Boolean.FALSE).sheet("sheet1").doWrite(excelExportDataDTO.getExportDataList());
    }

    /**
     * 获取导出的数据信息
     *
     * @param mergeCellsColumns
     * @param exportDataList
     * @param heads
     * @param size
     * @param writeHandler
     * @return {@link ExcelExportDataDTO}
     **/
    public static ExcelExportDataDTO getExcelExportData(int mergeCellsColumns, List<List<Object>> exportDataList, List<List<String>> heads, int size, WriteHandler writeHandler) {
        ExcelExportDataDTO excelExportDataDTO = new ExcelExportDataDTO();
        excelExportDataDTO.setExportDataList(exportDataList);
        excelExportDataDTO.setHeads(heads);
        excelExportDataDTO.setMergeCellsColumns(mergeCellsColumns);
        excelExportDataDTO.setTargetsSize(size);
        excelExportDataDTO.setWriteHandler(writeHandler);
        return excelExportDataDTO;
    }

    public static ExcelExportDataDTO getExcelExportData(List<List<Object>> exportDataList, List<List<String>> heads, WriteHandler writeHandler) {
        ExcelExportDataDTO excelExportDataDTO = new ExcelExportDataDTO();
        excelExportDataDTO.setExportDataList(exportDataList);
        excelExportDataDTO.setHeads(heads);
        excelExportDataDTO.setWriteHandler(writeHandler);
        return excelExportDataDTO;
    }

    /**
     * 直营预测——执行导出方法
     *
     * @param httpServletResponse
     * @param excelExportDataDTO
     */
    public static void exportExcelDirectlyExecute(HttpServletResponse httpServletResponse, ExcelExportDataDTO excelExportDataDTO) throws Exception {
        ExcelExportUtil.setCharacterAndReturnType(httpServletResponse, ExcelExportUtil.HEAD_CONTENT_TYPE_EXCEL);
        //合并单元格
        ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(httpServletResponse.getOutputStream()).head(excelExportDataDTO.getHeads()).registerWriteHandler(excelExportDataDTO.getWriteHandler()).registerWriteHandler(new DayLongestMatchColumnWidthStyleStrategy());
        //加入主要高级搜索条件的合并格式(这里的日期size写死为14)
        int i = 0;
        for (; i < excelExportDataDTO.getMergeCellsColumns(); i++) {
            LoopMergeStrategy loopMergeStrategy = new LoopMergeStrategy(excelExportDataDTO.getTargetsSize() * 14, i);
            ExcelExportUtil.METHOD_REGISTERWRITEHANDLER.invoke(excelWriterBuilder, loopMergeStrategy);
        }
        //加入日期的合并格式
        LoopMergeStrategy loopMergeStrategy = new LoopMergeStrategy(excelExportDataDTO.getTargetsSize(), i);
        ExcelExportUtil.METHOD_REGISTERWRITEHANDLER.invoke(excelWriterBuilder, loopMergeStrategy);
        excelWriterBuilder.autoCloseStream(Boolean.FALSE).sheet("sheet1").doWrite(excelExportDataDTO.getExportDataList());
    }


    /**
     * 导出excel模板数据
     *
     * @param httpServletResponse
     * @param excelExportDataDTO
     * @return void
     **/
    public static void exportExcelTemplate(HttpServletResponse httpServletResponse, ExcelExportDataDTO excelExportDataDTO) throws Exception {
        ExcelExportUtil.setCharacterAndReturnType(httpServletResponse, ExcelExportUtil.HEAD_CONTENT_TYPE_EXCEL);
        //合并单元格
        ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(httpServletResponse.getOutputStream()).head(excelExportDataDTO.getHeads()).registerWriteHandler(excelExportDataDTO.getWriteHandler()).registerWriteHandler(new com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy());
        excelWriterBuilder.autoCloseStream(Boolean.FALSE).sheet("sheet1").doWrite(excelExportDataDTO.getExportDataList());
    }

    /**
     * 导出excel到指定的文件夹
     *
     * @param excelExportDataDTO 需要导出的数据
     * @param folderName         文件夹名称
     * @param filePath           文件路径
     * @return String
     **/
    public static String exportExcel(ExcelExportDataDTO excelExportDataDTO, String folderName, String filePath, String fileName) throws Exception {
        // 获取系统文件分隔符。（在UNIX系统中是"/"，在WINDOWS系统中是"\"）
        String fileSeparator = System.getProperty("file.separator");
        filePath = filePath + fileSeparator + folderName;
        String yyyyMMdd = DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN);
        filePath = filePath + fileSeparator + yyyyMMdd;
        File file = new File(filePath);
        if (!file.exists() && !file.isDirectory()) {
            file.mkdirs();
        }
        fileName = fileName + SUFFIX_EXCEL_2007;
        filePath = filePath + fileSeparator + fileName;
        FileOutputStream fileOutputStream = new FileOutputStream(filePath);
        //合并单元格
        ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(fileOutputStream).head(excelExportDataDTO.getHeads())
                .registerWriteHandler(excelExportDataDTO.getWriteHandler())
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy());

        for (int i = 0; i < excelExportDataDTO.getMergeCellsColumns(); i++) {
            LoopMergeStrategy loopMergeStrategy = new LoopMergeStrategy(excelExportDataDTO.getTargetsSize(), i);
            ExcelExportUtil.METHOD_REGISTERWRITEHANDLER.invoke(excelWriterBuilder, loopMergeStrategy);
        }
        excelWriterBuilder.sheet("sheet1").doWrite(excelExportDataDTO.getExportDataList());
        return filePath;
    }

    public static String getFileName(String planType, Long userId, Integer exportType) {
        return planType + "-" + DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN) + "-" + userId + "-" + exportType;
    }
}
